package SSF.OS.OSPFv2.test;

/**
 * UnreliableIP.java
 *
 * @author Dirk Jacob
 * @version 0.2.1
 */

import java.util.*;
import SSF.OS.*;
import SSF.Net.*;
import com.renesys.raceway.DML.*;
import com.renesys.raceway.SSF.*;

/** This class is a replacement for SSF.OS.IP, from which the 
  * basic functionality is inherited. UnreliableIP adds the 
  * possibility to drop all packets which are received or should
  * be sent on a given interface at a given time. This behavior 
  * can be used to simulate broken links.
  */
public class UnreliableIP extends IP {
        
    /** The Fail class is a container for the configuration of a link failure. */
    class Fail {
    	/** start time */
		public double from;
		/** end time */
		public double until;
		/** associated interface */
		public int ifc;
    }

    /** stores all configured failure periods */
    private Vector failures; 
    
    /** the log utility */
	private Logger log = null;

	/** Constructor */
    public UnreliableIP() {

        /* first call the constructor of the base class */
		super();
	
		/* initialize the vector for the failure configuration */
		failures = new Vector();
	
		log = new Logger(Logger.DEBUG_NONE, this, "UnreliableIP");
    }
    
    /** Configure this ProtocolSession 
      * <pre>
      * ProtocolSession[ name ip use SSF.OS.OSPFv2.test.UnreliableIP
      *         fail[
      *             from <time>
      *             until <time>
      *             interface <id>
      *         ]
      *         ...
      * ]
      * </pre>
      */
    public void config(Configuration cfg) throws configException {
        
        /* first configure the standard IP class */
		super.config(cfg);
	
		/* now configure the log utility */
		log.config( cfg );

    	/* now, look for configured failures */
		for( Enumeration d = cfg.find("fail"); d.hasMoreElements(); ) {
	    	Configuration dcfg = (Configuration)d.nextElement();

	    	String from = (String)dcfg.findSingle("from");
	    	String until = (String)dcfg.findSingle("until");
	    	String ifc = (String)dcfg.findSingle("interface");

            /* only completely specified configs are taken */
	    	if( (from != null) && (until != null) && (ifc != null)) {
		
				/* create a Fail object for the specific failure */
				Fail failure = new Fail();
		
				failure.from = Double.parseDouble( from );
				failure.until = Double.parseDouble( until );
				failure.ifc = Integer.parseInt( ifc );
		
				/* and store it in the vector */
				failures.addElement( failure );
	    	}
		}
    }

	/** Handles incoming and outgoing IP-packets. Dependent of the interface
	  * and the simulation time, the packets are dropped or simply passed to
	  * IP.
	  */
    public boolean push(ProtocolMessage message, ProtocolSession fromSession)
		throws ProtocolException {

		IpHeader ipHeader = (IpHeader)message;

		/* look how late it is now ;) */
		double actual_time = (inGraph().now()) / SSF.Net.Net.frequency;
	
		/* now simply walk through all failure-periods and look, if the packet 
		   should be dropped or not */
		for (Enumeration e = failures.elements(); e.hasMoreElements(); ) {

	    	Fail act = (Fail)e.nextElement();

	    	if( (act.from <= actual_time) && (act.until >= actual_time) ) {
	    	
	    		/* we are in a failure period for some interface ...
	    	  	   now look into the packet, for which or from which inteface 
	    	       it comes */
	    		boolean drop_packet = false;
	    	
	    		/* 1. if the packet comes from an interface, look if it's the 
	    	  	      right one... */
	    		if (fromSession instanceof NIC) {
	    			if(act.ifc == ((NIC)fromSession).ID) {
	    				drop_packet = true;
	    			}    		
		    	}
	    	
	    		/* 2. if the packet does not come from the interface, we must 
	    	  	      look up the next hop interface for the packet. If the packet
	    	  	      will be sent out of the interface in question, it must also
	    	   	      be dropped */
	    	
	    		RoutingInfo rtgInfo = ROUTING_TABLE.findBest(ipHeader.SOURCE_IP,ipHeader.DEST_IP);
      		
      			if (rtgInfo != null) {
      				NIC use_iface = rtgInfo.next_hop_interface();
      			
	      			if(act.ifc == use_iface.ID) {
    	  				drop_packet = true;
      				}
      			}
	    	
	    		/* If the interface in question is used for this packet, drop 
	    	  	   the packet. */
	    		if( drop_packet ) {
					drop((IpHeader)message);
					String hostNhi = ((Host)inGraph()).nhi;
					log.logMessage("dropping packet on interface "+hostNhi+"("+ act.ifc +") !");
		
					return false;
				}
	    	}
		}
	
		/* When we are not in a failure period or the interface which fails is not
	  	   used for this packet, call IP's packet handler. */
		return super.push(message, fromSession);
    }
}
  